package com.e3.core.life;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.e3.core.utils.XMLUtils;

public class ApplicationLifecycleManager {

	private final static Map<String, ApplicationLifecycleManager.AppLifeListener> listenerMaps = new HashMap<String, ApplicationLifecycleManager.AppLifeListener>();
	
	private static List<AppLifecycleListener> listenerList = new ArrayList<AppLifecycleListener>();;
			
	private static final ApplicationLifecycleManager.Counter counter = new ApplicationLifecycleManager.Counter();
	
	private static String applicationName = "";
	
	private static boolean inited = false;
	
	private static boolean isStarted = false;
	
	private static List<AppLifecycleListener> getLifecycleListeners() {
		init();
		return listenerList;
	}

	
	private synchronized static void addListeners(String className,String description){
		if(listenerMaps.containsKey(className)){
			return;
		}
		try {
			AppLifecycleListener listener = (AppLifecycleListener)Class.forName(className).newInstance();
			listenerList.add(listener);
			
			ApplicationLifecycleManager.AppLifeListener appListener = new ApplicationLifecycleManager.AppLifeListener();
			appListener.setClassName(className);
			appListener.setDescription(description);
			appListener.setListener(listener);
			listenerMaps.put(className, appListener);
		} 
		catch(ClassNotFoundException e){
			System.err.println("Can't find the start class : "+className);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
		
	public synchronized static void init(){
		if(inited){
			return;
		}
		listenerMaps.clear();
		try {
			Document doc = XMLUtils.loadDocument(ApplicationLifecycleManager.class.getResourceAsStream("lifecycle.xml"));
			Element root = doc.getDocumentElement();
			applicationName = XMLUtils.getAtrributeValueOf(root,"app-name");
			List<Element> listenerNodes = XMLUtils.getElementsByTagName(root, "listener");
			for (Element element : listenerNodes) {
				String className = XMLUtils.getAtrributeValueOf(element, "class");
				String description = XMLUtils.getAtrributeValueOf(element, "description");
				addListeners(className, description);
			}
			inited = true;

		} catch (Exception e) {
			inited = false;
			e.printStackTrace();
			System.err.print("Load lifecycle listener configuration file failed");
		}
		
	}
	
	public synchronized static boolean start() {
		
		if(isStarted){
			System.out.print("\n"+attachAsterisk(applicationName+" has been started.")+"\n");
			return true;
		}
		
		System.out.print("\n"+attachAsterisk("Begin to start "+ applicationName +", please wait.")+"\n");
		boolean result = true;
		List<AppLifecycleListener> listeners = getLifecycleListeners();
		for (AppLifecycleListener listener : listeners) {
			String description = listenerMaps.get(listener.getClass().getName()).getDescription();

			String s = "\n" + description;
			System.out.print(s);
			
			long startTime = System.currentTimeMillis();
			
			StartStopWorker worker = new StartStopWorker(listener);
			counter.initCounter(s.length());
			innerStartStop(worker, true,counter);
			synchronized (applicationName) {
				try {
					while (!worker.isFinished()) {
						applicationName.wait();
					}
				} catch (InterruptedException e) {
				}
			}
			for(int i=0;i<counter.additionalPoint();i++){
				System.out.print(".");
			}

			long endTime = System.currentTimeMillis();
			long time = endTime - startTime;
			
			result = result==false?false:listener.isSuccess();
			System.out.print("" + (listener.isSuccess() ? "[OK]" : "[FAILED]"));
			System.out.print("[time:"+time+"ms]");
			
			if(!result){
				break;
			}
		}
		isStarted = true;
		
		if (result)
			System.out.print("\n\n"+attachAsterisk("Success to start "+applicationName+".")+"\n");
		else
			System.out.print("\n\n"+attachAsterisk("Faile to start "+applicationName+", please check the log for details.")+"\n");

		
		return result;
	}
	
	public static boolean isStarted(){
		return isStarted;
	}
	
	private static String attachAsterisk(String s){
		if(s==null){
			s = "";
		}
		int totalLength = 100;
		StringBuffer sb = new StringBuffer();
		int length = (totalLength-s.length())/2;
		for(int i=0;i<length;i++){
			sb.append("*");
		}
		
		sb.append(s);
		
		for(int i=0;i<length;i++){
			sb.append("*");
		}
		
		return sb.toString();
	}
	
	
	public synchronized static void stop() {
		List<AppLifecycleListener> listeners = getLifecycleListeners();
		
		for (AppLifecycleListener listener : listeners) {
			String description = listenerMaps.get(listener.getClass().getName()).getDescription();
			System.out.print("\n"+ description);
			listener.stop();
			System.out.print(""+(listener.isSuccess()?"[OK]":"[FAILED]")+"\n");
		}
	}
	private static void innerStartStop(final StartStopWorker worker,final boolean isStart,final Counter counter){
		Thread printDotThread = new Thread(new Runnable(){
			public void run() {
				try {				
					for(;;){
						Thread.sleep(1000);
						if(worker.isFinished()){
							synchronized (applicationName) {
								applicationName.notify();
							}
						}
						else{	
							if(counter.isNewline()){
								System.out.print("\n.");
							}
							else{
								System.out.print(".");
							}
							counter.add();
						}
					}
					
				} catch (Exception e) {
				}
				
			}
			
		});
		Thread startThread = new Thread(new Runnable(){			
			public void run() {
				try {
					if(isStart){
						worker.start();
					}
					else{
						worker.stop();
					}

				} catch (Exception e) {
				}
				
			}
			
		});
		
		startThread.start();
		printDotThread.start();

	}
	
	private static class Counter{
		private int count = 0;
		
		public void initCounter(int initLong){
			count = initLong-1;
		}
		
		public void add(){
			count++;
		}
		public int getCount(){
			return count;
		}
		public boolean isNewline(){
			return count!=0&&count%60==0?true:false;
		}
		public int additionalPoint(){
			return 60-count%60;
		}
	}

	private static class StartStopWorker{
		private AppLifecycleListener listener;
		public StartStopWorker(AppLifecycleListener listener){
			this.listener = listener;
		}
				
		private boolean isFinished = false;
		
		public void start(){
			listener.start();
			isFinished = true;
		}
		
		public void stop(){
			listener.stop();
			isFinished = true;
		}

		public boolean isFinished() {
			return isFinished;
		}

	}
	
	private static class AppLifeListener{
		private String className;
		
		private AppLifecycleListener listener;
		
		private String description;

		public String getClassName() {
			return className;
		}

		public void setClassName(String className) {
			this.className = className;
		}

		public String getDescription() {
			return description;
		}

		public void setDescription(String description) {
			this.description = description;
		}

		public AppLifecycleListener getListener() {
			return listener;
		}

		public void setListener(AppLifecycleListener listener) {
			this.listener = listener;
		}
		
	}
	
}
